home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / TPIFile / TPIFile.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  59.2 KB  |  1,830 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        TPIFile.c
  3.  
  4.     Contains:    TPI Module to access File Manager files.  Technology
  5.                 demonstration only!
  6.  
  7.     Written by:    Quinn "The Eskimo!"
  8.  
  9.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  10.  
  11.     Change History (most recent first):
  12.  
  13.     You may incorporate this sample code into your applications without
  14.     restriction, though the sample code has been provided "AS IS" and the
  15.     responsibility for its operation is 100% yours.  However, what you are
  16.     not permitted to do is to redistribute the source as "DSC Sample Code"
  17.     after having made changes. If you're going to re-distribute the source,
  18.     we require that you make it clear in the source that the code was
  19.     descended from Apple Sample Code, but that you've made changes.
  20.     
  21.     More assertions than an email meeting with Brian Bechtel!
  22. */
  23.  
  24. /////////////////////////////////////////////////////////////////////
  25. // The OT debugging macros in <OTDebug.h> require this variable to
  26. // be set.
  27.  
  28. #ifndef qDebug
  29. #define qDebug    1
  30. #endif
  31.  
  32. /////////////////////////////////////////////////////////////////////
  33. // Determine whether this is going to be an instrumented build or not.
  34.  
  35. #ifndef INSTRUMENTATION_ACTIVE
  36.     #define INSTRUMENTATION_ACTIVE 0
  37. #else
  38.     #define INSTRUMENTATION_ACTIVE 1
  39. #endif
  40.  
  41. /////////////////////////////////////////////////////////////////////
  42. // Pick up all the standard OT module stuff.
  43.  
  44. #include <OpenTptModule.h>
  45.  
  46. /////////////////////////////////////////////////////////////////////
  47. // You've gotta wonder why noCacheMask is only declared in <FSM.h>!
  48.  
  49. #include <FSM.h>
  50. #include <Files.h>
  51. #include <Devices.h>
  52.  
  53. /////////////////////////////////////////////////////////////////////
  54. // Pick up Instrumentation SDK stuff.  We only do this if we're
  55. // actually instrumenting, so you don't even have to have the SDK
  56. // to compile the non-instumented version of the code.  If we're
  57. // instrumenting, we compile a bunch of bogus macros that generally
  58. // compile to nothing.
  59.  
  60. #if INSTRUMENTATION_ACTIVE
  61.     #include <InstrumentationMacros.h>
  62. #else
  63.     #define TRACE_SETUP        long __junk
  64.     #define LOG_ENTRY(n)    if (0) { __junk ; }
  65.     #define LOG_EXIT        if (0) { __junk ; }
  66. #endif
  67.  
  68. /////////////////////////////////////////////////////////////////////
  69. // Pick up our module specific data structures, specifically
  70. // the AF_FILESPEC address format.
  71.  
  72. #include "TPIFile.h"
  73.  
  74. /////////////////////////////////////////////////////////////////////
  75.  
  76. extern pascal void OTDebugStr(const char *str)
  77.     // OTDebugStr seems to be missing from the latest OpenTptMiscUtilsPPC.o,
  78.     // so we implement our own dummy version here.
  79. {
  80.     debugstr(str);
  81. }
  82.  
  83. /////////////////////////////////////////////////////////////////////
  84.  
  85. #if INSTRUMENTATION_ACTIVE
  86.  
  87.     // If we're instrumenting, do a special hack to allow us
  88.     // to see calls to the File Manager.  We declare stub
  89.     // routines with a trailing "X" for each File Manager
  90.     // routine we call, and then macro define the corresponding
  91.     // identifiers to expand out to the "X" routine.
  92.     //
  93.     // In general you don't have to do this sort of rubbish
  94.     // when instrumenting your code.  The Instrumentation SDK
  95.     // describes how you can use MrProf to do an equivalent
  96.     // thing automagically.  I tried that and TPIFile started
  97.     // crashing unexpectedly.  I would love to know why, but
  98.     // alas I didn't have enough time to debug it.  Instead,
  99.     // I did this hack.  *sigh*
  100.  
  101.     static pascal OSErr PBHOpenAsyncX(HParmBlkPtr paramBlock)
  102.     {
  103.         TRACE_SETUP;
  104.         OSErr result;
  105.         
  106.         LOG_ENTRY( "TPIFile:PBHOpenAsync" );
  107.         result = PBHOpenAsync(paramBlock);
  108.         LOG_EXIT;
  109.         return (result);
  110.     }
  111.  
  112.     static pascal OSErr PBReadAsyncX(ParmBlkPtr paramBlock)
  113.     {
  114.         TRACE_SETUP;
  115.         OSErr result;
  116.         
  117.         LOG_ENTRY( "TPIFile:PBReadAsync" );
  118.         result = PBReadAsync(paramBlock);
  119.         LOG_EXIT;
  120.         return (result);
  121.     }
  122.  
  123.     static pascal OSErr PBCloseAsyncX(ParmBlkPtr paramBlock)
  124.     {
  125.         TRACE_SETUP;
  126.         OSErr result;
  127.         
  128.         LOG_ENTRY( "TPIFile:PBCloseAsync" );
  129.         result = PBCloseAsync(paramBlock);
  130.         LOG_EXIT;
  131.         return (result);
  132.     }
  133.  
  134.     #define PBHOpenAsync    PBHOpenAsyncX
  135.     #define PBReadAsync        PBReadAsyncX
  136.     #define PBCloseAsync    PBCloseAsyncX
  137.  
  138. #endif
  139.  
  140. /////////////////////////////////////////////////////////////////////
  141.  
  142. static mblk_t* qmi_tpi_data_ind(mblk_t* trailer_mp, int flags, long type)
  143.     // I found that mi_tpi_data_ind is not exported by the OT libraries,
  144.     // so I simply include my own verison here.
  145. {
  146.     mblk_t* mp;
  147.     
  148.     mp = mi_tpi_data_req(trailer_mp, flags, type);
  149.     if (mp)
  150.         ((struct T_data_ind *)mp->b_rptr)->PRIM_type = T_DATA_IND;
  151.     return mp;
  152. }
  153.  
  154. /////////////////////////////////////////////////////////////////////
  155.  
  156. static Boolean IsReadQ(queue_t* q)
  157.     // Returns true if q is the read queue of a queue pair.
  158. {
  159.     return ( (q->q_flag & QREADR) != 0 );
  160. }
  161.  
  162. static Boolean IsWriteQ(queue_t* q)
  163.     // Returns true if q is the write queue of a queue pair.
  164. {
  165.     return ( (q->q_flag & QREADR) == 0 );
  166. }
  167.  
  168. /////////////////////////////////////////////////////////////////////
  169.  
  170. // kDataBufferSize is the number of bytes we read of the file
  171. // at a time.  If you have a file that's larger than kDataBufferSize,
  172. // it gets sent upstream in kDataBufferSize chunks.  The number was
  173. // chosen because I know that OT doesn't like you asking for big
  174. // large individual memory buffers.  Remember, OT's memory manager
  175. // is designed for networking, and optimised for buffers that are
  176. // the size of your typical network packet.
  177.  
  178. enum {
  179.     kDataBufferSize = 2048
  180. };
  181.  
  182. /////////////////////////////////////////////////////////////////////
  183. // Per-Stream information
  184.  
  185. // This structure is used to hold the per-stream data for the module.
  186. // While module's can use normal global variables to store real globals,
  187. // they must maintain their own per-stream data structures.  I use
  188. // mi_open_comm to allocate this data structure when the stream is
  189. // opened.  mi_open_comm stores the address of this data structure in the
  190. // read and write queue's q_ptr field, so the rest of the code
  191. // can get to it by calling the GetPerStreamData function.
  192.  
  193.     // Due to an intensely annoying inability to distinguish three different
  194.     // states in DoDisconnectRequestAck, I need to define a sub-state variable
  195.     // that distinguishes between the possible ways to get to DoDisconnectRequestAck
  196.     // when the stream is in state TS_WACK_DREQ6.
  197.  
  198.     typedef enum {
  199.         WSS_OPEN = 0,            // Got to DoDisconnectRequestAck by getting a T_DISCON_REQ while in TS_WCON_CREQ.
  200.         WSS_ISSUE_MP            // Got to DoDisconnectRequestAck by calling CloseFileAndPostMessage.
  201.     } WackSubStateType;
  202.  
  203. struct PerStreamData
  204. {
  205.     OSType                 magic;                // kTPIFilePerStreamDataMagic = 'ESK0' for debugging
  206.  
  207.     long                   currentState;        // State of the TPI module, TS_UNBND etc
  208.  
  209.     WackSubStateType    wackSubstate;        // Sub-state while in TS_WACK_DREQ6, WSS_OPEN etc
  210.  
  211.     queue_t             *readQueue;            // Read queue for this stream.  Used by the
  212.                                             // the ioCompletion routine to find the queue.
  213.  
  214.     mblk_t                 *currentMessage;    // Current message pending ioCompletion.
  215.                                             // When the CustomIOCompletion routine fires,
  216.                                             // it puts this message on the above queue.
  217.  
  218.     HParamBlockRec        fileParamBlock;        // Current pending IOParamBlock
  219.  
  220.     FSSpec                 fileSpec;            // The file we're connected to.
  221.  
  222.     short                 fileRefNum;            // The fileRefNum if the file is open.
  223.  
  224.     Boolean                detached;            // Whether this stream has been detached.
  225.                                             // Streams get detached when they are closed
  226.                                             // but there is outstanding asynchronous I/O
  227.                                             // which we have to wait to complete before
  228.                                             // we can dispose of this data structure.
  229. };
  230. typedef struct PerStreamData PerStreamData, *PerStreamDataPtr;
  231.  
  232.  
  233. static PerStreamDataPtr GetPerStreamData(queue_t* readOrWriteQ)
  234.     // You can pass both the read or the write queue to this routine
  235.     // because mi_open_comm sets up both q_ptr's to point to the
  236.     // queue local data.
  237.     //
  238.     // Note that, in order to avoid the overhead of a function call,
  239.     // you would normally use inline code (or a macro)
  240.     // to get your per-stream data instead of using a separate function.
  241.     // However I think the separate function makes things clearer.
  242.     // I also acts as a central bottleneck for my debugging code.
  243.     //
  244.     // Environment: any standard STREAMS entry point
  245. {
  246.     PerStreamDataPtr streamData;
  247.     
  248.     streamData = (PerStreamDataPtr) readOrWriteQ->q_ptr;
  249.  
  250.     OTAssert("GetPerStreamData: what streamData", streamData != nil);
  251.     OTAssert("GetPerStreamData: Bad magic", streamData->magic == kTPIFilePerStreamDataMagic);
  252.     
  253.     return (streamData);
  254. }
  255.  
  256. // mi_open_comm and mi_close_comm (and also mi_detach and mi_close_detached)
  257. // use this global to store the list of open streams to this module.
  258.  
  259. static char* gModuleList = nil;
  260.  
  261. // A UPP for CustomIOCompletion.
  262.  
  263. static IOCompletionUPP gCustomIOCompletionUPP = nil;
  264.  
  265. /////////////////////////////////////////////////////////////////////
  266.  
  267. static void CloseFileAndPostMessage(queue_t* q, mblk_t* mp)
  268.     // This routine is called to close the open file associated
  269.     // with the stream the given queue (which is the read-side
  270.     // queue).  mp is either nil, or the message that should be
  271.     // posted to the read-side service routine when the close is
  272.     // complete.
  273.     // 
  274.     // Environment: read service routine
  275.     // Environment: Deferred Task (called by CleanUpPerStreamData)
  276.     // Environment: close entry point
  277. {
  278.     TRACE_SETUP;
  279.     PerStreamDataPtr streamData;
  280.     
  281.     LOG_ENTRY( "TPIFile:CloseFileAndPostMessage" );
  282.     OTAssert("CloseFileAndPostMessage: Not the read queue", IsReadQ(q) );
  283.     
  284.     streamData = GetPerStreamData(q);
  285.     OTAssert("CloseFileAndPostMessage: Already have a current message", streamData->currentMessage == nil);
  286.     OTAssert("CloseFileAndPostMessage: File isn't open", streamData->fileRefNum != 0);
  287.  
  288.     streamData->currentMessage = mp;
  289.  
  290.     // Set up the parameter block for the _Close.
  291.         
  292.     streamData->fileParamBlock.ioParam.ioRefNum = streamData->fileRefNum;
  293.     streamData->fileParamBlock.ioParam.ioCompletion = gCustomIOCompletionUPP;
  294.     
  295.     // This is critical.  Set our copy of file reference number to 0 so
  296.     // that DoDisconnectRequestAck knows that it's the second time it's been called.
  297.     
  298.     streamData->fileRefNum = 0;
  299.     
  300.     // Start the close operation.
  301.     
  302.     (void) PBCloseAsync( (ParmBlkPtr) &streamData->fileParamBlock);
  303.  
  304.     // ... continue in CustomIOCompletion... TPIFileReadService... ( DoDisconnectRequestAck or DoDisconnectIndication)...
  305.     LOG_EXIT;
  306. }
  307.  
  308. /////////////////////////////////////////////////////////////////////
  309.  
  310. static Boolean CleanUpPerStreamData(PerStreamDataPtr streamData)
  311.     // This routine is called to clean up the per-stream data for this
  312.     // stream.  The routine returns true if the stream is clean (ie
  313.     // the memory can be disposed of), or false if there is an
  314.     // async close operation that means the stream must be left lying around.
  315.     // If it returns false, the rest of the module organises to call
  316.     // it repeatably until it returns true.
  317.     //
  318.     // Environment: Deferred Task (scheduled by CustomIOCompletion)
  319.     // Environment: close entry point
  320.     // Environment: TerminateStreamModule
  321. {
  322.     TRACE_SETUP;
  323.     Boolean result;
  324.     
  325.     LOG_ENTRY( "TPIFile:CleanUpPerStreamData" );
  326.     
  327.     OTAssert("CleanUpPerStreamData: Already have a current message", streamData->currentMessage == nil);
  328.     
  329.     // Test to see whether the file is currently open.
  330.     
  331.     if ( (streamData->fileRefNum != 0) || (streamData->fileParamBlock.ioParam.ioResult > noErr) ) {
  332.  
  333.         // The file is open, or the close is still in progress.  If the close isn't
  334.         // in progress, start one.  Regardless, return false to let the caller know
  335.         // we can't be killed yet.
  336.         
  337.         if ( streamData->fileParamBlock.ioParam.ioResult <= noErr ) {
  338.             // OTDebugBreak("CleanUpPerStreamData: Executing the especially hard case");
  339.             CloseFileAndPostMessage(streamData->readQueue, nil);
  340.         }
  341.  
  342.         result = false;
  343.     } else {
  344.     
  345.         // The file is closed.  No other clean up required, but if streamData
  346.         // contained pointers to other data structures, this is where we would
  347.         // dispose them.
  348.         
  349.         result = true;
  350.     }
  351.     
  352.     LOG_EXIT;
  353.     return (result);
  354. }
  355.  
  356. /////////////////////////////////////////////////////////////////////
  357.  
  358. static long gCleanUpAnyDetachedStreamsTaskID = 0;
  359.  
  360. static pascal void CleanUpAnyDetachedStreams(void* junkArg)
  361.     // Loop through all of the streams looking for detached ones.
  362.     // When we find a detached stream, attempt to close it by
  363.     // calling CleanUpPerStreamData.
  364.     //
  365.     // When the ioCompletion fires but finds it hasn't got a
  366.     // message to work with (because the stream has closed) it
  367.     // schedules this deferred task to run and actually kill
  368.     // the stream.
  369.     //
  370.     // TerminateStreamModule also calls this routine
  371.     // to kill any remaining streams before the module quits.
  372.     //
  373.     // Environment: Deferred Task (scheduled by CustomIOCompletion)
  374.     // Environment: TerminateStreamModule
  375. {
  376.     TRACE_SETUP;
  377.     #pragma unused(junkArg)
  378.     PerStreamDataPtr thisStreamData;
  379.     
  380.     LOG_ENTRY( "TPIFile:CleanUpAnyDetachedStreams" );
  381.  
  382.     // OTDebugBreak("CleanUpAnyDetachedStreams: Entering");
  383.     
  384.     // Loop through each of the streams...
  385.     
  386.     thisStreamData = (PerStreamDataPtr) gModuleList;
  387.     while (thisStreamData != nil) {
  388.         if (thisStreamData->detached && CleanUpPerStreamData( thisStreamData ) ) {
  389.             
  390.             // Yes're allowed to kill this detached stream.
  391.         
  392.             mi_close_detached(&gModuleList, (char *) thisStreamData);
  393.             
  394.             // Resume the search at the beginning of the stream list because
  395.             // a) I don't know the exact ordering that mi_open_comm uses to
  396.             //    create the stream list, so I'll just do the entire thing again.
  397.             // b) There's a possibility that hardware interrupts are detaching
  398.             //    streams while we're working here, so we might as well pick
  399.             //    them up in this pass.
  400.             
  401.             thisStreamData = (PerStreamDataPtr) gModuleList;
  402.  
  403.         } else {
  404.         
  405.             // This stream is not detached or not eligible for closing, continue
  406.             // on with the next one.
  407.             thisStreamData = (PerStreamDataPtr) mi_next_ptr( (char *) thisStreamData );
  408.         }
  409.         
  410.     }
  411.     
  412.     // You might think that we can assert that we've mi_close_detached
  413.     // at least one stream here, but that may not be true.  Imagine this
  414.     // scenario:
  415.     //
  416.     // 1. CustomIOCompletion fires, marks stream detached, schedules
  417.     //    CleanUpAnyDetachedStreams
  418.     // 2. CleanUpAnyDetachedStreams runs, finds marked stream, 
  419.     //    and calls mi_close_detached on it.  It then resumes its
  420.     //    search at the beginning of the stream list.
  421.     // 3. CustomIOCompletion fires again, for a different stream.
  422.     //    It marks the stream detached and schedules
  423.     //    CleanUpAnyDetachedStreams again.  This works because
  424.     //    OTScheduleDeferredTask considers a running task (ie
  425.     //    CleanUpAnyDetachedStreams) to not be scheduled, so it
  426.     //    will schedule it again.
  427.     // 4. The first instance of CleanUpAnyDetachedStreams continues
  428.     //    running and picks up the second marked stream, which it closes.
  429.     // 5. The second instance of CleanUpAnyDetachedStreams runs,
  430.     //    and can't find a stream to close.
  431.     //
  432.     // The fact that OTScheduleDeferredTask will reschedule a running
  433.     // task is *good* because, although it means there's a possibility
  434.     // that we'll run CleanUpAnyDetachedStreams unnecessarily, it also
  435.     // guarantees that we'll never leave a dangling detached stream.
  436.     
  437.     LOG_EXIT;
  438. }
  439.  
  440. /////////////////////////////////////////////////////////////////////
  441.  
  442. static pascal void CustomIOCompletion(ParmBlkPtr paramBlock)
  443.     // This function is the ioCompletion routine we use for all of
  444.     // our File Manager operations.  Because it operates at hardware
  445.     // interrupt level, it is not synchronised with the rest of STREAMS
  446.     // and so we have to be careful that we only call STREAMS routines
  447.     // that are accessible from hardware interrupt level.  This list
  448.     // of routines is given in the "Open Tpt Module Dev. Note".
  449.     // 
  450.     // The routine performs two different functions based on whether
  451.     // currentMessage is nil or not.  If currentMessage is not nil,
  452.     // the routine simply puts that message on the stream's read-side
  453.     // queue.  This schedules the TPIFileReadService, which continues
  454.     // the processing of the operation based on the type of the message.
  455.     // The person who called the File Manager will have set currentMessage
  456.     // up appropriately.
  457.     //
  458.     // If currentMessage is nil, the stream was closed before the
  459.     // File Manager ioCompletion routine ran.  We have to take special
  460.     // care to clean up this stream correctly by scheduling the
  461.     // CleanUpAnyDetachedStreams deferred task.
  462.     //
  463.     // In looking at trace logs it seems that PBCloseAsync pretty much
  464.     // always operates synchronously, so this second case is most probably
  465.     // not well tested.
  466.     //
  467.     // Environment: Hardware Interrupt (ioCompletion from File Manager)
  468. {
  469.     TRACE_SETUP;
  470.     PerStreamDataPtr streamData;
  471.     mblk_t *mp;
  472.     
  473.     LOG_ENTRY( "TPIFile:CustomIOCompletion" );
  474.  
  475.     // Tell OT we're operating at hardware interrupt level.
  476.     
  477.     OTEnterInterrupt();
  478.     
  479.     // Get the per-stream data associated with this File Manager
  480.     // operation using the standard "offset from the ParamBlockRec"
  481.     // technique.
  482.     
  483.     streamData = (PerStreamDataPtr) ( ((UInt8 *) paramBlock) - OTOffsetOf(PerStreamData, fileParamBlock) );
  484.     OTAssert("CustomIOCompletion: Bad magic", streamData->magic == kTPIFilePerStreamDataMagic);
  485.     // OTAssert("CustomIOCompletion: This is not wrong, merely interesting", streamData->currentMessage != nil);
  486.     
  487.     mp = streamData->currentMessage;
  488.     
  489.     if (mp == nil) {
  490.         
  491.         // The stream was closed before the ioCompletion routine could fire.
  492.         // Schedule our deferred task to clean up the wreckage.
  493.         
  494.         OTAssert("CustomIOCompletion: Clean up deferred task not created", gCleanUpAnyDetachedStreamsTaskID != 0);
  495.         
  496.         streamData->detached = true;
  497.         
  498.         OTScheduleDeferredTask(gCleanUpAnyDetachedStreamsTaskID);
  499.         
  500.         // ... continue in CleanUpAnyDetachedStreams...
  501.         
  502.     } else {
  503.  
  504.         streamData->currentMessage = nil;
  505.         
  506.         // The stream is still a going proposition.  Put
  507.         // the current message on our read-side queue,
  508.         // which has the effect of scheduling our read-side
  509.         // service routine TPIFileReadService which will
  510.         // forward the message upstream.
  511.  
  512.         putq(streamData->readQueue, mp);
  513.  
  514.         // ... continue in TPIFileReadService ...
  515.     }
  516.     
  517.     OTLeaveInterrupt();
  518.     
  519.     LOG_EXIT;
  520. }
  521.  
  522. /////////////////////////////////////////////////////////////////////
  523. // Open routine
  524.  
  525. static SInt32 TPIFileOpen(queue_t* rdq, dev_t* dev, SInt32 flag, SInt32 sflag, cred_t* creds)
  526.     // This routine is called by STREAMS when a new stream is connected to
  527.     // our module.  The bulk of the work here is done by the Mentat helper
  528.     // routine mi_open_comm.
  529.     //
  530.     // Environment: standard STREAMS entry point
  531. {
  532.     TRACE_SETUP;
  533.     SInt32 err;
  534.     PerStreamDataPtr streamData;
  535.  
  536.     LOG_ENTRY( "TPIFile:TPIFileOpen" );
  537.     
  538.     OTAssert("TPIFileOpen: Not the read queue", IsReadQ(rdq) );
  539.  
  540.     err = noErr;
  541.     
  542.     // If we already have per-stream data for this stream, the stream is being reopened.
  543.     // In that case, we can just return.
  544.     // Note that we can't call GetPerStreamData because it checks that streamData is not nil.
  545.     
  546.     if ( rdq->q_ptr != nil ) {
  547.         goto done;
  548.     }
  549.  
  550.     // Make sure we're being opened properly -- because we're a driver we
  551.     // don't allow a "module" open.
  552.     
  553.     if ( err == noErr && sflag == MODOPEN ) {
  554.         err = ENXIO;
  555.     }
  556.     
  557.     // Use the mi_open_comm routine to allocate our per-stream data.  Then
  558.     // zero out the entire per-stream data record and fill out the fields
  559.     // we're going to need.
  560.     
  561.     if (err == noErr) {
  562.         err = mi_open_comm(&gModuleList, sizeof(PerStreamData), rdq, dev, flag, sflag, creds);
  563.         if ( err == noErr ) {
  564.             // Note that we can't call GetPerStreamData because the magic is not set up yet.
  565.             streamData = (PerStreamDataPtr) rdq->q_ptr;
  566.             
  567.             OTMemzero(streamData, sizeof(PerStreamData));
  568.             
  569.             streamData->magic = kTPIFilePerStreamDataMagic;
  570.             streamData->currentState = TS_UNBND;
  571.             streamData->readQueue = rdq;
  572.         }
  573.     }
  574.  
  575. done:
  576.     LOG_EXIT;
  577.     return (err);
  578. }
  579.  
  580. /////////////////////////////////////////////////////////////////////
  581. // Close routine
  582.  
  583. static SInt32 TPIFileClose(queue_t* rdq, SInt32 flags, cred_t* credP)
  584.     // This routine is called by STREAMS when a stream is being
  585.     // disconnected from our driver (ie closed).  The operation of this
  586.     // routine is complicated by one important fact: because we
  587.     // call File Manager asynchronously, there may be a File Manager
  588.     // I/O operation in progress when the stream is closed.  If this
  589.     // is the case, must use a special technique (mi_detach) to 
  590.     // detach our per-stream data from the actual stream, and 
  591.     // then organise to clean up this data at some later stage.
  592.     //
  593.     // Environment: standard STREAMS entry point
  594. {
  595.     TRACE_SETUP;
  596.     #pragma unused(flags)
  597.     #pragma unused(credP)
  598.     PerStreamDataPtr streamData;
  599.     mblk_t *oldCurrentMessage;
  600.  
  601.     LOG_ENTRY( "TPIFile:TPIFileClose" );
  602.     OTAssert("TPIFileClose: Not the read queue", IsReadQ(rdq) );
  603.  
  604.     streamData = GetPerStreamData(rdq);
  605.     
  606.     // Be very careful here.  streamData->currentMessage is also modified by
  607.     // the CustomIOCompletion routine, which (unlike the rest of our
  608.     // module) is not synchronised with this routine.  So we must use
  609.     // atomic operations to do this change streamData->currentMessage
  610.     // to make sure that either we close the stream or we detach the
  611.     // stream and CustomIOCompletion uses a deferred task to close it.
  612.     //
  613.     // Note that CustomIOCompletion does not have to use an atomic
  614.     // operation to modify streamData->currentMessage because it can
  615.     // interrupt us but we can't interrupt it.
  616.     
  617.     do {
  618.         oldCurrentMessage = streamData->currentMessage;
  619.     } while ( ! OTCompareAndSwapPtr(oldCurrentMessage, nil, &streamData->currentMessage) ); 
  620.     
  621.     if ( oldCurrentMessage != nil ) {
  622.  
  623.         // The ioCompletion routine hasn't fired yet, and if it does
  624.         // we've atomically guaranteed that it will know that this
  625.         // queue has been detached.  So we detach the queue and
  626.         // wait for the completion routine to schedule our deferred
  627.         // task.
  628.         
  629.         // OTDebugBreak("TPIFileClose: Executing the hard case");
  630.         
  631.         freemsg(oldCurrentMessage);
  632.  
  633.         mi_detach(rdq, (char *) streamData);
  634.         
  635.         // ... continue in CustomIOCompletion...
  636.     
  637.     } else {
  638.  
  639.         if ( CleanUpPerStreamData( streamData ) ) {
  640.  
  641.             // The easy case.  There is no outstanding ioCompletion routine,
  642.             // and the file is closed, so we can just shut down this stream.
  643.         
  644.             (void) mi_close_comm(&gModuleList, rdq);
  645.             
  646.         } else {
  647.         
  648.             // CleanUpPerStreamData has scheduled an async _Close,
  649.             // detach the stream and wait for it.
  650.         
  651.             mi_detach(rdq, (char *) streamData);
  652.  
  653.             // ... continue in CustomIOCompletion...
  654.         }
  655.  
  656.     }
  657.  
  658.     LOG_EXIT;
  659.     return (0);
  660. }
  661.  
  662. /////////////////////////////////////////////////////////////////////
  663.  
  664. enum {
  665.     kNoPrimitive = -1
  666. };
  667.  
  668. static long GetPrimitive(mblk_t* mp)
  669.     // GetPrimitive gets the TPI primitive out of a message block.
  670.     // It returns kNoPrimitive if the message block is of the wrong
  671.     // type or there is no TPI primitive.
  672.     //
  673.     // Environment: any standard STREAMS entry point
  674. {
  675.     if ((mp->b_datap->db_type == M_PROTO || mp->b_datap->db_type == M_PCPROTO) && MBLK_SIZE(mp) >= sizeof(long) ) {
  676.         return ( ( (union T_primitives*) mp->b_rptr)->type );
  677.     } else {
  678.         return ( kNoPrimitive );
  679.     }
  680. }
  681.  
  682. /////////////////////////////////////////////////////////////////////
  683.  
  684. static void DoInfoRequest(queue_t* q, mblk_t* mp)
  685.     // Handle a T_INFO_REQ TPI message by responding with
  686.     // a T_INFO_ACK message.
  687.     //
  688.     // Environment: write put routine
  689. {
  690.     TRACE_SETUP;
  691.     T_info_ack *infoAck;
  692.     PerStreamDataPtr streamData;
  693.  
  694.     LOG_ENTRY( "TPIFile:DoInfoRequest" );
  695.     OTAssert("DoInfoRequest: Not the write queue", IsWriteQ(q) );
  696.  
  697.     streamData = GetPerStreamData(q);
  698.     
  699.     // Allocate the T_INFO_ACK message, reusing mp if possible.
  700.     
  701.     mp = mi_tpi_ack_alloc(mp, sizeof(T_info_ack), T_INFO_ACK);
  702.     OTAssert("mi_tpi_ack_alloc failed", mp != nil );
  703.     
  704.     infoAck = (T_info_ack *) mp->b_rptr;
  705.     
  706.     // Fill out infoAck.  Note that we do not say we support
  707.     // orderly release!
  708.     
  709.     infoAck->TSDU_size = 0;
  710.     infoAck->ETSDU_size = T_INVALID;
  711.     infoAck->CDATA_size = T_INVALID;
  712.     infoAck->DDATA_size = T_INVALID;
  713.     infoAck->ADDR_size = T_INFINITE;
  714.     infoAck->OPT_size = T_INVALID;
  715.     infoAck->TIDU_size = T_INFINITE;
  716.     infoAck->SERV_type = T_COTS;
  717.     infoAck->CURRENT_state = streamData->currentState;
  718.     infoAck->PROVIDER_flag = 0;
  719.     
  720.     (void) qreply(q, mp);
  721.     LOG_EXIT;
  722. }
  723.  
  724. /////////////////////////////////////////////////////////////////////
  725.  
  726. static void ReplyWithErrorAck(queue_t* q, mblk_t* mp, long tpiError, long unixError)
  727.     // A simple routine which replies to a TPI message with a T_ERROR_ACK
  728.     // message containing the error codes tpiError and unixError.
  729.     //
  730.     // Environment: write put routine
  731. {
  732.     TRACE_SETUP;
  733.     
  734.     LOG_ENTRY( "TPIFile:ReplyWithErrorAck" );
  735.     OTAssert("ReplyWithErrorAck: Not the write queue", IsWriteQ(q) );
  736.  
  737.     mp = mi_tpi_err_ack_alloc(mp, tpiError, unixError);
  738.     OTAssert("mi_tpi_err_ack_alloc failed", mp != nil );
  739.     (void) qreply(q, mp);
  740.     LOG_EXIT;
  741. }
  742.  
  743. /////////////////////////////////////////////////////////////////////
  744.  
  745. static void DoBindRequest(queue_t* q, mblk_t* mp)
  746.     // Handle a T_BIND_REQ message by changing to the bound state
  747.     // and replying with a T_BIND_ACK message.
  748.     //
  749.     // Environment: write put routine
  750. {
  751.     TRACE_SETUP;
  752.     T_bind_req *bindReq;
  753.     T_bind_ack *bindAck;
  754.     PerStreamDataPtr streamData;
  755.  
  756.     LOG_ENTRY( "TPIFile:DoBindRequest" );
  757.     OTAssert("DoBindRequest: Not the write queue", IsWriteQ(q) );
  758.  
  759.     streamData = GetPerStreamData(q);
  760.     
  761.     // Check whether we're in the right state.
  762.     if (streamData->currentState != TS_UNBND) {
  763.         ReplyWithErrorAck(q, mp, TOUTSTATE, 0);
  764.         goto done;
  765.     }
  766.     
  767.     // Check the bind parameters.
  768.     bindReq = (T_bind_req *) mp->b_rptr;
  769.     
  770.     if (bindReq->CONIND_number != 0 || bindReq->ADDR_length != 0) {
  771.         ReplyWithErrorAck(q, mp, TNOADDR, 0);
  772.         goto done;
  773.     }
  774.  
  775.     // All is cool, lets say we're bound.
  776.     mp = mi_tpi_ack_alloc(mp, sizeof(T_bind_ack), T_BIND_ACK);
  777.     OTAssert("mi_tpi_ack_alloc failed", mp != nil );
  778.     
  779.     bindAck = (T_bind_ack *) mp->b_rptr;
  780.     
  781.     bindAck->ADDR_length = 0;
  782.     bindAck->ADDR_offset = sizeof(T_bind_ack);
  783.     bindAck->CONIND_number = 0;
  784.     
  785.     // Switch to the bound state.
  786.     
  787.     streamData->currentState = TS_IDLE;
  788.     
  789.     qreply(q, mp);
  790.  
  791. done:
  792.     LOG_EXIT;
  793.     return;
  794. }
  795.  
  796. /////////////////////////////////////////////////////////////////////
  797.  
  798. static void DoConnectRequest(queue_t* q, mblk_t* mp)
  799.     // Handle a T_CONN_REQ message in two stages.  If the connect
  800.     // request contains bogus information, we immediately NAK
  801.     // it with a T_ERROR_ACK.  If the information in the connect
  802.     // request looks OK, we start the connection process (ie by
  803.     // calling PBHOpenAsync) and send up a T_OK_ACK to say that
  804.     // the connection is in progress.  When the async operation
  805.     // completes, the ioCompletion fires, calling CustomIOCompletion
  806.     // which in turn puts streamData->currentMessage on the read-side
  807.     // queue, which schedule TPIFileReadService, which calls
  808.     // DoConnectConfirm which finally creates the T_CONN_CON message.
  809.     //
  810.     // Environment: write put routine
  811. {
  812.     TRACE_SETUP;
  813.     T_conn_req *connReq;
  814.     FileSpecAddressPtr connAddr;
  815.     PerStreamDataPtr streamData;
  816.  
  817.     LOG_ENTRY( "TPIFile:DoConnectRequest" );
  818.     OTAssert("DoConnectRequest: Not the write queue", IsWriteQ(q) );
  819.  
  820.     streamData = GetPerStreamData(q);
  821.     
  822.     // Check whether we're in the right state.
  823.     if (streamData->currentState != TS_IDLE) {
  824.         ReplyWithErrorAck(q, mp, TOUTSTATE, 0);
  825.         goto done;
  826.     }
  827.  
  828.     // Check the connect parameters.
  829.     connReq = (T_conn_req *) mp->b_rptr;
  830.     
  831.     if (connReq->OPT_length != 0) {
  832.         ReplyWithErrorAck(q, mp, TBADOPT, 0);
  833.         goto done;
  834.     }
  835.     if (connReq->DEST_length != sizeof(FileSpecAddress)) {
  836.         ReplyWithErrorAck(q, mp, TBADADDR, 0);
  837.         goto done;
  838.     }
  839.     connAddr = (FileSpecAddressPtr) mi_offset_paramc(mp, connReq->DEST_offset, connReq->DEST_length);
  840.     if (connAddr->fAddressType != AF_FILESPEC) {
  841.         ReplyWithErrorAck(q, mp, TBADADDR, 0);
  842.         goto done;
  843.     }
  844.  
  845.     streamData->fileSpec = connAddr->fss;
  846.  
  847.     // Create the T_CONN_CON message for use by the ioCompletion routine.
  848.     
  849.     OTAssert("DoConnectRequest: Already have a current message", streamData->currentMessage == nil);
  850.     streamData->currentMessage = mi_tpi_conn_con(nil, (char *) connAddr, sizeof(FileSpecAddress), nil, 0);
  851.     OTAssert("DoConnectRequest: mi_tpi_conn_con failed", streamData->currentMessage != nil);
  852.  
  853.     // mi_tpi_conn_con fills in all the fields of the message, so we have nothing more to do.
  854.     
  855.     streamData->currentState = TS_WCON_CREQ;
  856.     
  857.     streamData->fileParamBlock.ioParam.ioVRefNum = streamData->fileSpec.vRefNum;
  858.     streamData->fileParamBlock.fileParam.ioDirID = streamData->fileSpec.parID;
  859.     streamData->fileParamBlock.ioParam.ioNamePtr = &streamData->fileSpec.name[0];
  860.     streamData->fileParamBlock.ioParam.ioPermssn = fsRdPerm;
  861.     streamData->fileParamBlock.ioParam.ioVersNum = 0;
  862.     streamData->fileParamBlock.ioParam.ioMisc = nil;
  863.     streamData->fileParamBlock.ioParam.ioCompletion = gCustomIOCompletionUPP;
  864.     (void) PBHOpenAsync(&streamData->fileParamBlock);
  865.     // Throw away error result from PBOpenAsync because File Manager will still call
  866.     //  our completion routine.
  867.     
  868.     // We've started the connection attempt, respond with a T_OK_ACK.
  869.     
  870.     mp = mi_tpi_ok_ack_alloc(mp);
  871.     OTAssert("mi_tpi_ok_ack_alloc failed", mp != nil );
  872.  
  873.     qreply(q, mp);    
  874.  
  875.     // ... continue in CustomIOCompletion... TPIFileReadService... DoConnectConfirm...
  876.  
  877. done:
  878.     LOG_EXIT;
  879.     return;    
  880. }
  881.  
  882. /////////////////////////////////////////////////////////////////////
  883.  
  884. static void DoDisconnectRequest(queue_t* q, mblk_t* mp)
  885.     // Handling a T_DISCON_REQ message is complicated by two
  886.     // factors: a) there are two possible states that the stream
  887.     // can be in and get a valid T_DISCON_REQ, and we have to
  888.     // handle both cases slightly differently, and b) there
  889.     // could be a pending asynchronous I/O operation for this
  890.     // stream, and we don't want it going upstream after we've
  891.     // disconnected.
  892.     //
  893.     // To handle the first difficulty, we have to case our
  894.     // behaviour on the current state of the stream.
  895.     // If the stream is in TS_WCON_CREQ (Waiting for Confirmation
  896.     // of Connection Request -- we were asked to disconnect
  897.     // while we were in the process of connecting), we switch
  898.     // to state TS_WACK_DREQ6.  If the state is TS_DATA_XFER
  899.     // (Data Transfer -- we were asked to disconnect while
  900.     // we are tranferring data), we switch to TS_WACK_DREQ9.
  901.     //
  902.     // This resulting state is important because DoDisconnectRequestAck
  903.     // uses it to determine the next state in the state machine.
  904.     //
  905.     // We can't just send the T_OK_ACK upstream from here because
  906.     // we most probably have an outstanding File Manager completion
  907.     // routine (and it's hard to tell because File Manager completions
  908.     // are not synchronised with STREAMS) for the _Read or _Open,
  909.     // and we don't want that to complete at an unexpected
  910.     // time.  In addition, there is no way of cancelling
  911.     // outstanding File Manager requests.
  912.     //
  913.     // In the case where there is a pending File Manager operation,
  914.     // we simply swap out the current message and replace it with
  915.     // our T_OK_ACK message.  This must be done atomically to ensure
  916.     // that the completion routine doesn't fire while we're doing it.
  917.     //
  918.     // In the case where there is no pending File Manager operation,
  919.     // we post the message to the read-side queue directly.  This
  920.     // especially important in the case when the stream in in
  921.     // TS_DATA_XFER, because the read-side queue may be descheduled
  922.     // because of flow control.  Fortunately the T_OK_ACK message
  923.     // is a high-priority message, so it will be processed anyway.
  924.     //
  925.     // Environment: write put routine
  926. {
  927.     TRACE_SETUP;
  928.     PerStreamDataPtr streamData;
  929.     mblk_t* oldCurrentMessage;
  930.  
  931.     LOG_ENTRY( "TPIFile:DoDisconnectRequest" );
  932.     // OTDebugBreak("DoDisconnectRequest: Disconnect requested");
  933.  
  934.     OTAssert("DoDisconnectRequest: Not the write queue", IsWriteQ(q) );
  935.  
  936.     streamData = GetPerStreamData(q);
  937.  
  938.     // Do the right thing depending on the current state.
  939.     
  940.     switch ( streamData->currentState ) {
  941.         case TS_WCON_CREQ:
  942.         case TS_DATA_XFER:
  943.         
  944.             // Set the new state appropriately, so that 
  945.             // DoDisconnectRequestAck knows what to do.
  946.  
  947.             if ( streamData->currentState == TS_WCON_CREQ ) {
  948.                 streamData->wackSubstate = WSS_OPEN;
  949.                 streamData->currentState = TS_WACK_DREQ6;
  950.             } else {
  951.                 streamData->currentState = TS_WACK_DREQ9;
  952.             }
  953.  
  954.             // Allocate an appropriate T_OK_ACK message.
  955.  
  956.             mp = mi_tpi_ok_ack_alloc(mp);
  957.             OTAssert("DoDisconnectRequest: mi_tpi_ok_ack_alloc failed", mp != nil );
  958.             
  959.             // Swap it into this stream's currentMessage field.
  960.  
  961.             oldCurrentMessage = streamData->currentMessage;
  962.             if ( (oldCurrentMessage != nil ) && 
  963.                         OTCompareAndSwapPtr(oldCurrentMessage, mp, &streamData->currentMessage) ) {
  964.                 // We successfully managed to swap the message into the
  965.                 // current message field.  The T_OK_ACK message will be
  966.                 // posted when the ioCompletion runs.
  967.                 
  968.                 freemsg(oldCurrentMessage);
  969.             } else {
  970.                 OTAssert("DoDisconnectRequest: Message unexpectedly appeared", streamData->currentMessage == nil);
  971.                 
  972.                 // There is either no outstanding I/O operation, or it fired
  973.                 // while we were looking at it.  We must do clever things!
  974.                 // namely putting our T_OK_ACK message on the read-side queue.
  975.                 //
  976.                 // Because this is a high-priority message it will be delivered
  977.                 // before the T_CONN_CON or T_DATA_IND messages that might have
  978.                 // been queued by the ioCompletion routine before it in the stream
  979.                 // read-side queue.
  980.                 
  981.                 putq( RD(q) , mp);
  982.  
  983.                 // btw We do not freemsg(oldCurrentMessage) because the interrupt
  984.                 // handler has already put it on our read-side queue.
  985.             }
  986.             
  987.             // ... continue in DoDisconnectRequestAck...
  988.  
  989.             break;
  990.  
  991.         default:
  992.             OTDebugBreak("DoDisconnectRequest: Out of state");
  993.             ReplyWithErrorAck(q, mp, TOUTSTATE, 0);
  994.             break;
  995.     }
  996.     LOG_EXIT;
  997. }
  998.  
  999. /////////////////////////////////////////////////////////////////////
  1000.  
  1001. static void DoUnbindRequest(queue_t* q, mblk_t* mp)
  1002.     // Handle a T_UNBIND_REQ message.  TPI says that
  1003.     // we can only get these in the TS_IDLE idle state.
  1004.     // If that's the case, we simply change state to
  1005.     // TS_UNBND and reply with a T_OK_ACK.
  1006.     //
  1007.     // Environment: write put routine
  1008. {
  1009.     TRACE_SETUP;
  1010.     PerStreamDataPtr streamData;
  1011.  
  1012.     LOG_ENTRY( "TPIFile:DoUnbindRequest" );
  1013.     OTAssert("DoUnbindRequest: Not the write queue", IsWriteQ(q) );
  1014.  
  1015.     streamData = GetPerStreamData(q);
  1016.  
  1017.     // Check whether we're in the right state.
  1018.     if (streamData->currentState != TS_IDLE) {
  1019.         ReplyWithErrorAck(q, mp, TOUTSTATE, 0);
  1020.         goto done;
  1021.     }
  1022.  
  1023.     // No other parameters to check, so everything is cool.
  1024.     // We change state and ACK the request.
  1025.     
  1026.     streamData->currentState = TS_UNBND;
  1027.     
  1028.     mp = mi_tpi_ok_ack_alloc(mp);
  1029.     OTAssert("DoUnbindRequest: mi_tpi_ok_ack_alloc failed", mp != nil );
  1030.  
  1031.     qreply(q, mp);
  1032.  
  1033. done:
  1034.     LOG_EXIT;
  1035. }
  1036.  
  1037. /////////////////////////////////////////////////////////////////////
  1038.  
  1039. static void DoOptionManagementRequest(queue_t* q, mblk_t* mp)
  1040.     // We've told the world that we don't support option management
  1041.     // so just error any T_OPTMGMT_REQ messages.
  1042.     //
  1043.     // Environment: write put routine
  1044. {
  1045.     TRACE_SETUP;
  1046.     OTAssert("DoOptionManagementRequest: Not the write queue", IsWriteQ(q) );
  1047.  
  1048.     LOG_ENTRY( "TPIFile:DoOptionManagementRequest" );
  1049.     ReplyWithErrorAck(q, mp, TBADOPT, 0);
  1050.     LOG_EXIT;
  1051. }
  1052.  
  1053. /////////////////////////////////////////////////////////////////////
  1054.  
  1055. static void DoStreamFatalError(queue_t* q, mblk_t* mp)
  1056.     // Send up a stream-fatal M_ERROR message.  We do this in response
  1057.     // to messages that are just wrong for our type of stream.
  1058.     //
  1059.     // Environment: write put routine
  1060. {
  1061.     TRACE_SETUP;
  1062.     
  1063.     OTDebugBreak("DoStreamFatalError");
  1064.  
  1065.     LOG_ENTRY( "TPIFile:DoStreamFatalError" );
  1066.     OTAssert("DoStreamFatalError: Not the write queue", IsWriteQ(q) );
  1067.     OTAssert("DoStreamFatalError: mp is nil", mp != nil );
  1068.     freemsg(mp);
  1069.     
  1070.     // Allocate a 1 byte M_ERROR message containing EPROTO
  1071.     // and send it back up the stream.  This basically kills
  1072.     // the stream as far as the client is concerned.  Normally
  1073.     // you wouldn't do this except in dire circumstances.  I
  1074.     // do it when I find that someone has sent me the wrong
  1075.     // kind of message.  Normally the stream head should protect
  1076.     // you against this.
  1077.     
  1078.     mp = allocb(sizeof(UInt8), 0);
  1079.     OTAssert("DoStreamFatalError: allocb failed", mp != nil );
  1080.     OTAssert("DoStreamFatalError: allocb is not doing what we expect", mp->b_rptr == mp->b_wptr);
  1081.     
  1082.     mp->b_datap->db_type = M_ERROR;
  1083.     
  1084.     *((UInt8 *) (mp->b_wptr)) = EPROTO;
  1085.     mp->b_wptr = mp->b_rptr + 1;
  1086.  
  1087.     qreply(q, mp);
  1088.     LOG_EXIT;
  1089. }
  1090.  
  1091. /////////////////////////////////////////////////////////////////////
  1092.  
  1093. static void DoDataRequest(queue_t* q, mblk_t* headerBlock, mblk_t* dataBlock)
  1094.     // Handle a T_DATA_REQ message.  Well actually, we don't handle
  1095.     // them, so we kill the stream if we get one.
  1096.     //
  1097.     // headerBlock may be nil when this messages is caused by an M_DATA
  1098.     // message.
  1099.     //
  1100.     // Environment: write put routine
  1101. {
  1102.     TRACE_SETUP;
  1103.     mblk_t* mp;
  1104.  
  1105.     LOG_ENTRY( "TPIFile:DoDataRequest" );
  1106.  
  1107.     OTAssert("DoDataRequest: Not the write queue", IsWriteQ(q) );
  1108.  
  1109.     if ( headerBlock == nil ) {
  1110.         mp = dataBlock;
  1111.     } else {
  1112.         mp = headerBlock;
  1113.     }
  1114.     
  1115.     // We do not handle writing data to TPIFile streams.
  1116.     DoStreamFatalError(q, headerBlock);
  1117.     LOG_EXIT;
  1118. }
  1119.  
  1120. /////////////////////////////////////////////////////////////////////
  1121.  
  1122. static void DoFlushRequest(queue_t* q, mblk_t* mp)
  1123.     // Handle an M_FLUSH message as per "STREAMS Modules and Drivers", p8-12.
  1124.     //
  1125.     // Environment: write put routine
  1126. {    
  1127.     TRACE_SETUP;
  1128.     
  1129.     LOG_ENTRY( "TPIFile:DoFlushRequest" );
  1130.     OTAssert("DoFlushRequest: Not the write queue", IsWriteQ(q) );
  1131.  
  1132.     // If we've been asked to flush the write queue, just go and do it.
  1133.     
  1134.     if ( ( *(mp->b_rptr) & FLUSHW ) != 0 ) {
  1135.         flushq(q, FLUSHDATA);
  1136.     }
  1137.  
  1138.     // Check whether we've been asked to flush the read queue.
  1139.  
  1140.     if ( ( *(mp->b_rptr) & FLUSHR ) != 0 ) {
  1141.         
  1142.         // If so, clear the FLUSHW bit and send the flush
  1143.         // request back up stream.  We don't actually flush
  1144.         // the contents of the read queue because doing so 
  1145.         // might compromise the delicate state machine in
  1146.         // this module.  I need to investigate whether this
  1147.         // is a serious problem and, if it is, work out a fix.
  1148.  
  1149.         *(mp->b_rptr) &= ~FLUSHW;
  1150.         qreply(q, mp);
  1151.         
  1152.     } else {
  1153.  
  1154.         // We weren't asked to flush the read queue, so
  1155.         // we're done with this message.
  1156.  
  1157.         freemsg(mp);
  1158.     }
  1159.     LOG_EXIT;
  1160. }
  1161.  
  1162. /////////////////////////////////////////////////////////////////////
  1163. // Write-side put routine
  1164.  
  1165. static SInt32 TPIFileWritePut(queue_t* q, mblk_t* mp)
  1166.     // This routine is called by STREAMS when it has a message for our
  1167.     // module.  This routine is basically a big case statement that
  1168.     // dispatches to our various message handling routines.
  1169.     // 
  1170.     // Note that a production module would probably want to handle
  1171.     // the high frequency requests (like T_DATA_REQ) inline in this
  1172.     // routine for maximum speed, but this module is still in the
  1173.     // "make it work" stage.
  1174.     //
  1175.     // Environment: standard STREAMS entry point
  1176. {
  1177.     TRACE_SETUP;
  1178.     PerStreamDataPtr streamData;
  1179.     struct iocblk *iocblkPtr;
  1180.     
  1181.     LOG_ENTRY( "TPIFile:TPIFileWritePut" );
  1182.  
  1183.     OTAssert("TPIFileWritePut: Not the write queue", IsWriteQ(q) );
  1184.  
  1185.     streamData = GetPerStreamData(q);
  1186.  
  1187.     switch ( mp->b_datap->db_type ) {
  1188.         case M_IOCTL:
  1189.             // "STREAMS Modules and Drivers" p8-33 says: "A driver must process
  1190.             // an M_IOCTL message.  Otherwise, the Stream head blocks for an M_IOCNAK
  1191.             // or M_IOCACK until the timeout (potentially infinite) expires. If a driver
  1192.             // does not understand an ioctl, an M_IOCNAK message must be sent to upstream."
  1193.  
  1194.             // I'm not sure why we're allowed to reuse this datab without
  1195.             // check it for read-only, but everyone does so!
  1196.             
  1197.             mp->b_datap->db_type = M_IOCNAK;
  1198.             iocblkPtr = (struct iocblk *) mp->b_rptr;
  1199.             iocblkPtr->ioc_error = EINVAL;
  1200.  
  1201.             qreply(q, mp);
  1202.             break;
  1203.         
  1204.         case M_DATA:
  1205.             // "STREAMS Modules and Drivers", Appendix A-2, T_DATA_REQ (7tpi) says:
  1206.             // "The transport provider must also recognize a message of one or more
  1207.             // M_DATA message blocks without the leading M_PROTO message block as a 
  1208.             // T_DATA_REQ primitive. This message type will be initiated from the write 
  1209.             // (BA_OS) operating system service routine."
  1210.  
  1211.             DoDataRequest(q, nil, mp);
  1212.             break;
  1213.             
  1214.         case M_FLUSH:
  1215.             DoFlushRequest(q, mp);
  1216.             break;
  1217.  
  1218.         default:
  1219.             switch ( GetPrimitive(mp) ) {
  1220.                 case T_INFO_REQ:
  1221.                     DoInfoRequest(q, mp);
  1222.                     break;
  1223.                 case T_BIND_REQ:
  1224.                     DoBindRequest(q, mp);
  1225.                     break;
  1226.                 case T_CONN_REQ:
  1227.                     DoConnectRequest(q, mp);
  1228.                     break;
  1229.                 case T_DISCON_REQ:
  1230.                     DoDisconnectRequest(q, mp);
  1231.                     break;
  1232.                 case T_UNBIND_REQ:
  1233.                     DoUnbindRequest(q, mp);
  1234.                     break;
  1235.                 case T_OPTMGMT_REQ:
  1236.                     DoOptionManagementRequest(q, mp);
  1237.                     break;
  1238.                 case T_DATA_REQ:
  1239.                     DoDataRequest(q, mp, mp->b_cont);
  1240.                     break;
  1241.                 
  1242.                 // Standard TPI messages that are inappropriate.
  1243.                 case T_ORDREL_REQ:
  1244.                 case T_UNITDATA_REQ:
  1245.                 case T_EXDATA_REQ:
  1246.                 
  1247.                 // Extended TPI messages that are inappropriate.
  1248.                 case T_ADDR_REQ:
  1249.                 
  1250.                 // Transaction TPI messages that are inappropriate.
  1251.                 case T_UREQUEST_REQ:
  1252.                 case T_REQUEST_REQ:
  1253.                 case T_UREPLY_REQ:
  1254.                 case T_REPLY_REQ:
  1255.                 case T_CANCELREQUEST_REQ:
  1256.                 case T_CANCELREPLY_REQ:
  1257.  
  1258.                 // Mapper TPI messages that are inappropriate.
  1259.                 case T_REGNAME_REQ:
  1260.                 case T_DELNAME_REQ:
  1261.                 case T_LKUPNAME_REQ:
  1262.  
  1263.                     DoStreamFatalError(q, mp);
  1264.                     break;
  1265.  
  1266.                 case kNoPrimitive:
  1267.                 default:
  1268.                     // "STREAMS Modules and Drivers" p8-33 says: "Messages that are
  1269.                     // not understood by the driver should be freed."
  1270.                     
  1271.                     OTDebugBreak("TPIFileWritePut: Message not understood, freeing");
  1272.                     
  1273.                     freemsg(mp);
  1274.                     break;
  1275.             }
  1276.             break;
  1277.     }
  1278.     
  1279.     LOG_EXIT;
  1280.     
  1281.     return 0;
  1282. }
  1283.  
  1284. /////////////////////////////////////////////////////////////////////
  1285. // Read-side put routine
  1286.  
  1287. static SInt32 TPIFileReadPut(queue_t* q, mblk_t* mp)
  1288.     // Because we're a driver (ie at the end of the stream) this routine
  1289.     // should never be called by STREAMS.
  1290.     //
  1291.     // Environment: standard STREAMS entry point
  1292. {
  1293.     PerStreamDataPtr streamData;
  1294.     
  1295.     OTAssert("TPIFileReadPut: Not the read queue", IsReadQ(q) );
  1296.  
  1297.     streamData = GetPerStreamData(q);
  1298.  
  1299.     switch ( GetPrimitive(mp) ) {
  1300.         default:
  1301.             OTDebugBreak("TPIFileReadPut: Was called!");
  1302.             break;
  1303.     }
  1304.  
  1305.     return 0;
  1306. }
  1307.  
  1308. /////////////////////////////////////////////////////////////////////
  1309.  
  1310. static void StartDataIndication(queue_t* q)
  1311.     // StartDataIndication is called by either DoConnectionConfirm
  1312.     // or DoDataIndication to start a read request for a file.
  1313.     // The routine allocates a buffer for the T_DATA_IND message
  1314.     // and then starts an asynchronous File Manager _Read
  1315.     // request to read data into that buffer.  When the _Read
  1316.     // completes, CustomIOCompletion is called.  It puts 
  1317.     // the T_DATA_IND message on the read-side queue, which schedules
  1318.     // TPIFileReadService, which in turn calls DoDataIndication to
  1319.     // send the data upstream, and then calls StartDataIndication
  1320.     // again to start the next block.
  1321.     //
  1322.     // Environment: read service routine
  1323. {
  1324.     TRACE_SETUP;
  1325.     PerStreamDataPtr streamData;
  1326.     mblk_t* mp = nil;
  1327.     mblk_t* dataBuffer = nil;
  1328.  
  1329.     LOG_ENTRY( "TPIFile:StartDataIndication" );
  1330.  
  1331.     OTAssert("StartDataIndication: Not the read queue", IsReadQ(q) );
  1332.     
  1333.     streamData = GetPerStreamData(q);
  1334.     OTAssert("StartDataIndication: Already have a current message", streamData->currentMessage == nil);
  1335.     OTAssert("StartDataIndication: Wrong state", streamData->currentState == TS_DATA_XFER);
  1336.  
  1337.     // Create a data buffer for the data to be read.
  1338.     
  1339.     dataBuffer = allocb(kDataBufferSize, 0);
  1340.     OTAssert("StartDataIndication: allocb failed", dataBuffer != nil);
  1341.  
  1342.     // Create a T_DATA_IND message with that data buffer.
  1343.     
  1344.     mp = qmi_tpi_data_ind(dataBuffer, 0, 0);
  1345.     OTAssert("StartDataIndication: qmi_tpi_data_ind failed", mp != nil);
  1346.  
  1347.     // Remember the T_DATA_IND message as the current message, ie the
  1348.     // one that CustomIOCompletion will operate on.
  1349.     
  1350.     streamData->currentMessage = mp;
  1351.     
  1352.     // Start the File Manager read request.
  1353.  
  1354.     streamData->fileParamBlock.ioParam.ioRefNum = streamData->fileRefNum;
  1355.     streamData->fileParamBlock.ioParam.ioBuffer = (char *) dataBuffer->b_datap->db_base;
  1356.     streamData->fileParamBlock.ioParam.ioReqCount = dataBuffer->b_datap->db_lim - dataBuffer->b_datap->db_base;
  1357.     streamData->fileParamBlock.ioParam.ioPosMode = fsAtMark + noCacheMask;
  1358.     streamData->fileParamBlock.ioParam.ioPosOffset = 0;
  1359.     streamData->fileParamBlock.ioParam.ioCompletion = gCustomIOCompletionUPP;
  1360.     (void) PBReadAsync( (ParmBlkPtr) &streamData->fileParamBlock);
  1361.     // Throw away error result from PBReadAsync because File Manager will still call
  1362.     //  our completion routine.
  1363.     
  1364.     // ... continue in CustomIOCompletion... TPIFileReadService... DoDataIndication...
  1365.     LOG_EXIT;
  1366. }
  1367.  
  1368. /////////////////////////////////////////////////////////////////////
  1369.  
  1370. static void DoConnectConfirm(queue_t* q, mblk_t* mp)
  1371.     // Handle processing of a T_CONN_CON message.  This message was originally
  1372.     // created in DoConnectRequest and put into the per-stream data
  1373.     // variable currentMessage.  When the ioCompletion for the PBHOpenAsync
  1374.     // fired, the CustomIOCompletion fired and queued the message on to the
  1375.     // read-side queue.  The TPIFileReadService then called this routine.
  1376.     // 
  1377.     // Because the stream is still in TS_WCON_CREQ, the routine just sends
  1378.     // the T_CONN_CON upstream to indicate to the client that we have
  1379.     // a connection in place (or a T_DISCON_IND if the file failed to open).
  1380.     // 
  1381.     // Environment: read service routine
  1382. {
  1383.     TRACE_SETUP;
  1384.     PerStreamDataPtr streamData;
  1385.  
  1386.     LOG_ENTRY( "TPIFile:DoConnectConfirm" );
  1387.  
  1388.     OTAssert("DoConnectConfirm: Not the read queue", IsReadQ(q) );
  1389.     
  1390.     streamData = GetPerStreamData(q);
  1391.     OTAssert("DoConnectConfirm: Already have a current message", streamData->currentMessage == nil);
  1392.  
  1393.     OTAssert("DoConnectConfirm: Wrong state", streamData->currentState == TS_WCON_CREQ);
  1394.  
  1395.     // This is the final stage of the connection process.  If the
  1396.     // PBHOpenAsync failed, free the T_CONN_CON and send a T_DISCON_IND
  1397.     // upstream instead.
  1398.     // If the open succeed, start the process of data transfer and send
  1399.     // the T_CONN_CON upstream.
  1400.     
  1401.     if (streamData->fileParamBlock.fileParam.ioResult != noErr) {
  1402.         freemsg(mp);
  1403.         mp = mi_tpi_discon_ind(nil, streamData->fileParamBlock.fileParam.ioResult, -1);
  1404.         OTAssert("DoConnectConfirm: mi_tpi_discon_ind failed", mp != nil);
  1405.         streamData->currentState = TS_IDLE;
  1406.     } else {
  1407.         streamData->fileRefNum = streamData->fileParamBlock.fileParam.ioFRefNum;
  1408.         streamData->currentState = TS_DATA_XFER;
  1409.         StartDataIndication(q);
  1410.     }
  1411.     putnext(q, mp);
  1412.     LOG_EXIT;
  1413. }
  1414.  
  1415. /////////////////////////////////////////////////////////////////////
  1416.  
  1417. static void DoDataIndication(queue_t* q, mblk_t* mp)
  1418.     // Handle processing of a T_DATA_IND message.  This message was originally
  1419.     // created in StartDataIndication and put into the per-stream data
  1420.     // variable currentMessage.  When the ioCompletion for the PBReadAsync
  1421.     // fired, the CustomIOCompletion fired and queued the message on to the
  1422.     // read-side queue.  The TPIFileReadService then called this routine.
  1423.     //
  1424.     // Because the stream is still in TS_DATA_XFER, the routine just sends
  1425.     // the T_DATA_IND upstream to indicate to the client the arrival of
  1426.     // new data (or a T_DISCON_IND if the read failed).
  1427.     //
  1428.     // Environment: read service routine
  1429. {
  1430.     TRACE_SETUP;
  1431.     PerStreamDataPtr streamData;
  1432.     
  1433.     LOG_ENTRY( "TPIFile:DoDataIndication" );
  1434.  
  1435.     OTAssert("DoDataIndication: Not the read queue", IsReadQ(q) );
  1436.  
  1437.     streamData = GetPerStreamData(q);
  1438.     OTAssert("DoDataIndication: Already have a current message", streamData->currentMessage == nil);
  1439.  
  1440.     OTAssert("DoDataIndication: Wrong state", streamData->currentState == TS_DATA_XFER);
  1441.  
  1442.     // If we get an eofErr then check whether we read any data at all
  1443.     // -- if we did, return the data to the user and then start another
  1444.     // read attempt, which will fail and finally cause the T_DISCON_IND to be
  1445.     // issued
  1446.         
  1447.     if (streamData->fileParamBlock.ioParam.ioResult == eofErr &&     
  1448.             streamData->fileParamBlock.ioParam.ioActCount != 0) {
  1449.         streamData->fileParamBlock.ioParam.ioResult = noErr;
  1450.     }
  1451.  
  1452.     // If we get an error, free the T_DATA_IND and send a T_DISCON_IND upstream instead.
  1453.     
  1454.     if (streamData->fileParamBlock.ioParam.ioResult != noErr) {
  1455.         freemsg(mp);
  1456.         mp = mi_tpi_discon_ind(nil, streamData->fileParamBlock.ioParam.ioResult, -1);
  1457.         OTAssert("DoDataIndication: mi_tpi_discon_ind failed", mp != nil);
  1458.  
  1459.         CloseFileAndPostMessage(q, mp);
  1460.         
  1461.         // ... continue in CustomIOCompletion... TPIFileReadService... DoDisconnectIndication...
  1462.  
  1463.     } else {
  1464.         OTAssert("DoDataIndication: No data in data message", mp->b_cont != nil);
  1465.         mp->b_cont->b_rptr = mp->b_cont->b_datap->db_base;
  1466.         mp->b_cont->b_wptr = mp->b_cont->b_datap->db_base + streamData->fileParamBlock.ioParam.ioActCount;
  1467.         putnext(q, mp);
  1468.         StartDataIndication(q);
  1469.     }
  1470.  
  1471.     LOG_EXIT;
  1472. }
  1473.  
  1474. /////////////////////////////////////////////////////////////////////
  1475.  
  1476. static void DoDisconnectRequestAck(queue_t* q, mblk_t* mp)
  1477.     // This routine is called when a T_OK_ACK message shows up on
  1478.     // the read-side queue.  This message was put there
  1479.     // by DoDisconnectRequest, which also switched the state to either
  1480.     // TS_WACK_DREQ9 or TS_WACK_DREQ6 depending on the state that
  1481.     // the stream was in when the T_DISCON_REQ arrived.
  1482.     //
  1483.     // See the comments in each branch of the case statement for
  1484.     // how these different cases are handled.
  1485.     //
  1486.     // Environment: read service routine
  1487. {
  1488.     TRACE_SETUP;
  1489.     PerStreamDataPtr streamData;
  1490.  
  1491.     LOG_ENTRY( "TPIFile:DoDisconnectRequestAck" );
  1492.  
  1493.     OTAssert("DoDisconnectRequestAck: Not the read queue", IsReadQ(q) );
  1494.     
  1495.     OTAssert("DoDisconnectRequestAck: Unexpected message primitive", 
  1496.             ( (GetPrimitive(mp) == T_OK_ACK) && (( (struct T_ok_ack *) mp->b_rptr)->CORRECT_prim == T_DISCON_REQ) )
  1497.         );
  1498.  
  1499.     streamData = GetPerStreamData(q);
  1500.     OTAssert("DoDisconnectRequestAck: Already have a current message", streamData->currentMessage == nil);
  1501.  
  1502.     switch ( streamData->currentState ) {
  1503.         case TS_WACK_DREQ6:
  1504.             // We've been asked to disconnect during the connection process,
  1505.             // ie between when we got the T_CONN_REQ and when the PBHOpenAsync
  1506.             // completed.  In this case, we ACK the T_DISCON_REQ and go back to
  1507.             // TS_IDLE state.  Except it's a bit more complicated than that...
  1508.  
  1509.             switch ( streamData->wackSubstate ) {
  1510.             
  1511.                 case WSS_OPEN:
  1512.                     // This is the first time we've hit this routine.  Check to see
  1513.                     // whether the file opened successfully.  If it did, we have
  1514.                     // to organise to close it before sending up the T_OK_ACK.
  1515.                     // We switch the wackSubState to WSS_ISSUE_MP to ensure that,
  1516.                     // the next time we come to this routine, we know that we've
  1517.                     // already closed the file.
  1518.                 
  1519.                     OTAssert("DoDisconnectRequestAck: File should be closed", streamData->fileRefNum == 0);
  1520.                     if (streamData->fileParamBlock.fileParam.ioResult != noErr) {
  1521.                         // If we got an error from the PBHOpenAsync, the file is not open, so we can
  1522.                         // just ACK the disconnect request immediately.
  1523.                         
  1524.                         streamData->currentState = TS_IDLE;
  1525.                         putnext(q, mp);
  1526.                         
  1527.                     } else {
  1528.                         
  1529.                         // The file is now open, but we have to close it before we
  1530.                         // can ACK the disconnect request.  So start the async
  1531.                         // close request.
  1532.                         
  1533.                         streamData->fileRefNum = streamData->fileParamBlock.fileParam.ioFRefNum;
  1534.                         
  1535.                         streamData->wackSubstate = WSS_ISSUE_MP;
  1536.                         CloseFileAndPostMessage(q, mp);
  1537.                         // ... continue in CustomIOCompletion... TPIFileReadService... DoDisconnectRequestAck
  1538.                         // ie the WSS_ISSUE_MP branch of this case statement
  1539.                     }
  1540.                     break;
  1541.  
  1542.                 case WSS_ISSUE_MP:
  1543.                     // This is the second time we've been called.  We've closed the
  1544.                     // file, so just go ahead and send the T_OK_ACK upstream to *finally*
  1545.                     // ACK the T_DISCON_REQ.
  1546.                     
  1547.                     OTAssert("DoDisconnectRequestAck: PBClose failed with error", streamData->fileParamBlock.fileParam.ioResult == noErr);
  1548.                     OTAssert("DoDisconnectRequestAck: File should be closed", streamData->fileRefNum == 0);
  1549.                     streamData->currentState = TS_IDLE;
  1550.                     putnext(q, mp);
  1551.                     break;
  1552.                 
  1553.                 default:
  1554.                     OTDebugBreak("DoDisconnectRequestAck: wackSubstate out of range");
  1555.                     break;
  1556.             }
  1557.             break;
  1558.         
  1559.         case TS_WACK_DREQ9:
  1560.             // If the stream is in TS_WACK_DREQ9, then we've received a T_DISCON_REQ
  1561.             // while we were in the TS_DATA_XFER state, ie we've been asked to disconnect
  1562.             // during the reading process.  Any pending reads have now
  1563.             // completed, so we can just send the T_OK_ACK upstream to ACK the
  1564.             // T_DISCON_REQ.  Well almost )-:  If the file is open we must close
  1565.             // it before doing this.
  1566.  
  1567.             if ( streamData->fileRefNum != 0 ) {
  1568.                 CloseFileAndPostMessage(q, mp);
  1569.                 // ... continue in CustomIOCompletion... TPIFileReadService... DoDisconnectRequestAck
  1570.                 // ie the other branch of this if statement
  1571.             } else {
  1572.                 streamData->currentState = TS_IDLE;
  1573.                 putnext(q, mp);
  1574.             }
  1575.             
  1576.             break;
  1577.             
  1578.         default:
  1579.             OTDebugBreak("DoDisconnectRequestAck: Unexpected state");
  1580.             break;
  1581.     }
  1582.     LOG_EXIT;
  1583. }
  1584.  
  1585. /////////////////////////////////////////////////////////////////////
  1586.  
  1587. static void DoDisconnectIndication(queue_t* q, mblk_t* mp)
  1588.     // This routine is called when a T_DISCON_IND message shows up
  1589.     // on the read-side queue.  This message was put there by
  1590.     // DoDataIndication in response to running into an error reading
  1591.     // the file.  We just switch states and forward the message
  1592.     // upstream.
  1593.     //
  1594.     // Environment: read service routine
  1595. {
  1596.     TRACE_SETUP;
  1597.     PerStreamDataPtr streamData;
  1598.  
  1599.     LOG_ENTRY( "TPIFile:DoDisconnectIndication" );
  1600.  
  1601.     OTAssert("DoDisconnectIndication: Not the read queue", IsReadQ(q) );
  1602.     
  1603.     OTAssert("DoDisconnectIndication: Unexpected message primitive", 
  1604.             ( (GetPrimitive(mp) == T_DISCON_IND) )
  1605.         );
  1606.  
  1607.     streamData = GetPerStreamData(q);
  1608.     OTAssert("DoDisconnectIndication: currentMessage should be nil", streamData->currentMessage == nil);
  1609.     
  1610.     OTAssert("DoDisconnectIndication: Wrong state", streamData->currentState == TS_DATA_XFER);
  1611.     OTAssert("DoDisconnectIndication: File should be closed", streamData->fileRefNum == 0);
  1612.  
  1613.     streamData->currentState = TS_IDLE;
  1614.     putnext(q, mp);
  1615.     LOG_EXIT;
  1616. }
  1617.  
  1618. /////////////////////////////////////////////////////////////////////
  1619. // Read-side service routine
  1620.  
  1621. static SInt32 TPIFileReadService(queue_t* q)
  1622.     // This routine is called by STREAMS when someone puts a message
  1623.     // on the read-side queue.  Seeing as we're a driver, the only person
  1624.     // who should be putting messages on our queue.  Some of these messages
  1625.     // have a special meaning, more than what the TPI defines, so we can't
  1626.     // just unilaterally send them upstream.  We have to dispatch them out
  1627.     // to the appropriate handler, which deals with the special cases.
  1628.     //
  1629.     // For example, when the completion routine for a PBReadAsync fires,
  1630.     // it simply queues the T_DATA_IND message on this queue.  However
  1631.     // no one has checked whether the read was successful yet, so we
  1632.     // can't just forward the T_DATA_IND upstream.  Instead this routine
  1633.     // sees the T_DATA_IND and forwards it to DoDataIndication, which 
  1634.     // checks the results of the read and does the right thing.
  1635.     //
  1636.     // Also note that, by handling the data transfer operations through
  1637.     // a this queue, we automatically become a STREAMS flow control
  1638.     // good citizen, just by following the standard STREAMS structure
  1639.     // for a service routine.
  1640.     //
  1641.     // Environment: read service routine
  1642. {
  1643.     TRACE_SETUP;
  1644.     mblk_t *mp;
  1645.     
  1646.     LOG_ENTRY( "TPIFile:TPIFileReadService" );
  1647.  
  1648.     OTAssert("TPIFileReadService: Not the read queue", IsReadQ(q) );
  1649.  
  1650.     // Standard STREAMS flow control structure.  Don't blame me for its bad style (-:
  1651.     
  1652.     while ( (mp = getq(q)) != nil ) {
  1653.         
  1654.         if ((mp->b_datap->db_type < QPCTL) && !canputnext(q)) {
  1655.             putbq(q, mp);
  1656.             goto done;
  1657.         }
  1658.         
  1659.         switch ( GetPrimitive(mp) ) {
  1660.             case T_CONN_CON:
  1661.                 DoConnectConfirm(q, mp);
  1662.                 break;
  1663.             case T_DATA_IND:
  1664.                 DoDataIndication(q, mp);
  1665.                 break;
  1666.             case T_OK_ACK:
  1667.                 DoDisconnectRequestAck(q, mp);
  1668.                 break;
  1669.             case T_DISCON_IND:
  1670.                 DoDisconnectIndication(q, mp);
  1671.                 break;
  1672.             default:
  1673.                 OTDebugBreak("TPIFileReadService: Unexpected type");
  1674.                 break;
  1675.         }
  1676.     
  1677.     }
  1678.  
  1679. done:
  1680.     LOG_EXIT;
  1681.     return (0);
  1682. }
  1683.  
  1684. /////////////////////////////////////////////////////////////////////
  1685. // Static Declaration Structures
  1686.  
  1687. static struct module_info gModuleInfo =  
  1688. {
  1689.     9990,                // Module Number, only useful for debugging
  1690.     "TPIFile",            // Name of module
  1691.     0,                    // Minimum data size
  1692.     INFPSZ,                // Maximum data size
  1693.     65536,                // Hi water mark for queue
  1694.     32768                // Lo water mark for queue
  1695. };
  1696.  
  1697. static struct qinit gReadInit = 
  1698. {
  1699.     TPIFileReadPut,        // Put routine for "incoming" data
  1700.     TPIFileReadService,    // Service routine for "incoming" data
  1701.     TPIFileOpen,        // Our open routine
  1702.     TPIFileClose,         // Our close routine
  1703.     nil,                // No admin routine
  1704.     &gModuleInfo        // Our module_info
  1705. };
  1706.  
  1707. static struct qinit gWriteInit =
  1708. {
  1709.     TPIFileWritePut,    // Put routine for client data
  1710.     nil,                // Service routine for client data
  1711.     nil,                // open  field only used in read-side structure
  1712.     nil,                // close field only used in read-side structure
  1713.     nil,                // admin field only used in read-side structure
  1714.     &gModuleInfo        // Our module_info
  1715. };
  1716.  
  1717. static struct streamtab theStreamTab = 
  1718. {
  1719.     &gReadInit,            // Our read-side qinit structure
  1720.     &gWriteInit,        // Our write-side qinit structure
  1721.     0,                    // We are not a mux, so set this to nil
  1722.     0                    // We are not a mux, so set this to nil
  1723. };
  1724.  
  1725. /////////////////////////////////////////////////////////////////////
  1726. // Macintosh-specific Static Structures
  1727.  
  1728. static struct install_info theInstallInfo =
  1729. {
  1730.     &theStreamTab,            // Stream Tab pointer
  1731.     kOTModIsDriver + kOTModUpperIsTPI,
  1732.                             // Tell OT that we are a driver, not a module
  1733.     SQLVL_MODULE,            // Synchronization level, module level for the moment
  1734.     0,                        // Shared writer list buddy
  1735.     0,                        // Open Transport use - always set to 0
  1736.     0                        // Flag - always set to 0
  1737. };
  1738.  
  1739. // Prototypes for the exported routines below.
  1740.  
  1741. extern Boolean InitStreamModule(TPIFilePortInfoRecordPtr portInfo);
  1742. extern void TerminateStreamModule(void);
  1743. extern install_info* GetOTInstallInfo();
  1744.  
  1745. #pragma export list InitStreamModule, TerminateStreamModule, GetOTInstallInfo
  1746.  
  1747. // Export entry point
  1748.  
  1749. extern Boolean InitStreamModule(TPIFilePortInfoRecordPtr portInfo)
  1750.     // Initialises the module.  Always called at SystemTask time,
  1751.     // so we can call NewIOCompletionProc.
  1752. {    
  1753.     TRACE_SETUP;
  1754.     Boolean result;
  1755.     
  1756.     OTDebugBreak("TPIFile: InitStreamModule");
  1757.     
  1758.     LOG_ENTRY( "TPIFile:InitStreamModule" );
  1759.     OTAssert("InitStreamModule: Bad magic in TPIFilePortInfoRecord",
  1760.                         (portInfo->magic1 == kTPIFilePortInfoMagic1) && 
  1761.                         (portInfo->magic2 == kTPIFilePortInfoMagic2)
  1762.             );
  1763.     #if ! qDebug
  1764.         // portInfo is only used in the above assertion, which is compiled out
  1765.         // if we're not debugging.
  1766.         #pragma unused(portInfo)
  1767.     #endif
  1768.     
  1769.     gCustomIOCompletionUPP = NewIOCompletionProc(CustomIOCompletion);
  1770.     gCleanUpAnyDetachedStreamsTaskID = OTCreateDeferredTask(CleanUpAnyDetachedStreams, nil);
  1771.     
  1772.     result = (gCustomIOCompletionUPP != nil && gCleanUpAnyDetachedStreamsTaskID != 0);
  1773.     
  1774.     LOG_EXIT;
  1775.     return (result);
  1776. }
  1777.  
  1778. extern void TerminateStreamModule(void)
  1779.     // Shuts down the module.  Always called at SystemTask time,
  1780.     // so we can call DisposeRoutineDescriptor.
  1781. {
  1782.     TRACE_SETUP;
  1783.     OSStatus err;
  1784.     
  1785.     LOG_ENTRY( "TPIFile:TerminateStreamModule" );
  1786.     
  1787.     if (gCustomIOCompletionUPP != nil) {
  1788.         DisposeRoutineDescriptor(gCustomIOCompletionUPP);
  1789.         gCustomIOCompletionUPP = nil;
  1790.     }
  1791.     if (gCleanUpAnyDetachedStreamsTaskID != 0) {
  1792.         err = OTDestroyDeferredTask(gCleanUpAnyDetachedStreamsTaskID);
  1793.         OTAssert("TerminateStreamModule: OTDestroyDeferredTask failed", err == noErr);
  1794.         gCleanUpAnyDetachedStreamsTaskID = 0;
  1795.     }
  1796.     
  1797.     // The purpose of gCleanUpAnyDetachedStreamsTaskID is to clean up any
  1798.     // detached streams that are pending the completetion of an asynchronous
  1799.     // _Close.  Unfortunately, in the case where the last stream is closed,
  1800.     // the TerminateStreamModule routine will be executed before the deferred
  1801.     // task runs, so we have just destroyed our last chance of cleaning up
  1802.     // the stream.  We obviously have to avoid this case, so we sit here
  1803.     // waiting for those _Close's to complete.  This is legal because we are
  1804.     // allowed to block inside the TerminateStreamModule routine.
  1805.     //
  1806.     // Well almost...  If the file we're accessing is on AppleShare, this
  1807.     // causes the machine to deadlock.  This is because we're waiting for
  1808.     // PBCloseAsync to complete, but it can't complete until we leave this
  1809.     // routine because AppleShare relies on OT to deliver packets to
  1810.     // complete the request and OT won't be delivering packets until we
  1811.     // leave this routine.  The upshot is that we deadlock.
  1812.  
  1813.     while ( gModuleList != nil ) {
  1814.         CleanUpAnyDetachedStreams(nil);
  1815.     }
  1816.     
  1817.     // While this assert can never trigger on my module (because of the previous
  1818.     // while loop), it's an excellent idea to have one of these in your code.
  1819.     
  1820.     OTAssert("TerminateStreamModule: Streams are still active", gModuleList == nil);
  1821.  
  1822.     LOG_EXIT;
  1823. }
  1824.  
  1825. extern install_info* GetOTInstallInfo()
  1826.     // Return pointer to install_info to STREAMS.
  1827. {
  1828.     return &theInstallInfo;
  1829. }
  1830.